/*
 * tsk_lcd.c
 *  Source for Melt MT-20S4 LCD routine
 *  Created on: 21.03.2011
 *      Author: Vladimir Meshkov <glavmonter@gmail.com>
 */

#include <string.h>
#include <stdio.h>
#include <stdint.h>

#include <stm32f10x.h>

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include "platform_config.h"

#include "tsk_lcd.h"
#include "hd44780.h"

#define stackDepth	(configMINIMAL_STACK_SIZE*2)

#define LCD_PINS_DATA	(LCD_D0_PIN | LCD_D1_PIN | LCD_D2_PIN | LCD_D3_PIN)
#define LCD_PINS_CONF	(LCD_A0_PIN | LCD_E_PIN | LCD_RW_PIN)

static portTASK_FUNCTION_PROTO( vTaskLCD, pvParameters );

typedef struct xLCDMESSAGE
{
	char 			cText[LCD_MAXLEN+1];
	uint8_t 		ucPosition;
	uint8_t 		ucTextLen;
	LCD_DataType	xDataType;
} xLCDMessage;

xQueueHandle xLCDQueue;

//static char 	sLines[4][LCD_MAXLEN + 1];
static uint8_t globalPosition = 0;

/*----------------------------------------------------------------------------------------------------------------------*/
static void _delay_ms( int ms )
{
int i;
	for( ; ms > 0; ms-- )
		for( i = 5000; i > 0; i-- )
			asm volatile ("nop");
}

/*----------------------------------------------------------------------------------------------------------------------*/
static void DrawString( char *str, uint8_t maxlen )
{
int i;

	if( maxlen > LCD_MAXLEN )
		return;

	portENTER_CRITICAL();
	for( i = 0; i < maxlen; i++ )
	{
		if( str[i] == 0 )
			hd44780_outdata( 0x20 ); // Пробел
		else
			hd44780_outdata( str[i] );
		_delay_ms(1);
		globalPosition++;
	}
	portEXIT_CRITICAL();
}

/*----------------------------------------------------------------------------------------------------------------------*/
portBASE_TYPE xLCDDrawSimple( const char *text )
{
	if( xLCDQueue == NULL )
		return LCD_QUEUE_ERR;

xLCDMessage message;
uint8_t len;
	len = strnlen( text, LCD_MAXLEN );
	//if( ( globalPosition + len ) > LCD_MAXLEN )
	//	len = LCD_MAXLEN - globalPosition;

	memset( &message, 0, sizeof( message ) );
	message.ucPosition = globalPosition;
	message.xDataType = DT_Text;
	memcpy( message.cText, text, len );
	message.ucTextLen = len;
	xQueueSend( xLCDQueue, ( void * ) &message, 1 );

	return LCD_OK;
}

/*----------------------------------------------------------------------------------------------------------------------*/
portBASE_TYPE xLCDDrawText( const char *text, LCD_Line line, uint8_t position )
{
xLCDMessage message;
uint8_t len;

	if( xLCDQueue == NULL )
		return LCD_QUEUE_ERR;

	if( position >= LCD_MAXLEN )
		return LCD_ERR_POSITION;

	globalPosition = line + position;
	memset( &message, 0, sizeof( message ) );
	message.ucPosition 	= globalPosition;

	len = strnlen( text, LCD_MAXLEN );
	if( ( position + len ) > LCD_MAXLEN )
		len = LCD_MAXLEN - position;

	memcpy( message.cText, text, len );
	message.ucTextLen 	= len;
	message.xDataType	= DT_Text;
	xQueueSend( xLCDQueue, ( void * ) &message, 1 );

	return LCD_OK;
}

portBASE_TYPE xLCDDrawTextFromISR( const char *text, LCD_Line line, uint8_t position )
{
xLCDMessage message;
uint8_t len;
signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	if( xLCDQueue == NULL )
		return LCD_QUEUE_ERR;

	if( position >= LCD_MAXLEN )
		return LCD_ERR_POSITION;

	globalPosition = line + position;
	memset( &message, 0, sizeof( message ) );
	message.ucPosition 	= globalPosition;


	len = strnlen( text, LCD_MAXLEN );
	if( ( position + len ) > LCD_MAXLEN )
		len = LCD_MAXLEN - position;

	memcpy( message.cText, text, len );
	message.ucTextLen 	= len;
	message.xDataType	= DT_Text;
	xQueueSendFromISR( xLCDQueue, ( void * ) &message, &xHigherPriorityTaskWoken );

	if( xHigherPriorityTaskWoken )
		taskYIELD();

	return LCD_OK;
}

/*----------------------------------------------------------------------------------------------------------------------*/
portBASE_TYPE xLCDClearText( LCD_Line line, uint8_t position, uint8_t lenght )
{
xLCDMessage message;
uint8_t len;

	if( xLCDQueue == NULL )
		return LCD_QUEUE_ERR;

	if( ( position >= LCD_MAXLEN ) || ( lenght == 0 ) )
		return LCD_ERR_POSITION;

	memset( &message, 0, sizeof( message ) );
	globalPosition = position;

	if( ( position + lenght ) > LCD_MAXLEN )
		len =  LCD_MAXLEN - position;
	else
		len = lenght;

	globalPosition = line + position;
	memset( message.cText, 0x20, len );
	message.ucPosition 	= globalPosition;
	message.ucTextLen 	= len;
	message.xDataType	= DT_Text;
	//message.xLine		= line;
	xQueueSend( xLCDQueue, ( void * ) &message, 1 );

	return LCD_OK;
}

portBASE_TYPE xLCDClearTextFromISR( LCD_Line line, uint8_t position, uint8_t lenght )
{
xLCDMessage message;
uint8_t len;
signed portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

	if( xLCDQueue == NULL )
		return LCD_QUEUE_ERR;

	if( ( position >= LCD_MAXLEN ) || ( lenght == 0 ) )
		return LCD_ERR_POSITION;

	memset( &message, 0, sizeof( message ) );
	globalPosition = line + position;

	if( ( position + lenght ) > LCD_MAXLEN )
		len =  LCD_MAXLEN - position;
	else
		len = lenght;
	memset( message.cText, 0x20, len );
	message.ucPosition 	= globalPosition;
	message.ucTextLen 	= len;
	message.xDataType	= DT_Text;
	xQueueSendFromISR( xLCDQueue, ( void * ) &message, &xHigherPriorityTaskWoken );

	if( xHigherPriorityTaskWoken )
		portYIELD();

	return LCD_OK;
}


/*----------------------------------------------------------------------------------------------------------------------*/
portBASE_TYPE xLCDSendCmd( LCD_DataType cmd, uint8_t argv )
{
	if( xLCDQueue == NULL )
		return LCD_QUEUE_ERR;

	if( cmd == DT_Text )
		return LCD_DATATYPE_ERR;

xLCDMessage message;
	memset( &message, 0, sizeof( message ) );
	message.xDataType = DT_CMD;
	message.cText[0] = cmd;
	message.cText[1] =  argv;

	xQueueSend( xLCDQueue, ( void * ) &message, 1 );

	return LCD_OK;
}

/*---------------------------------------------------------------------------------------------------------------------*/
void vStartLCD( unsigned portBASE_TYPE uxPriority )
{
	// Configure GPIOs for LCD
	GPIO_InitTypeDef GPIO_InitStructure;

	// Data pins: D0-D3
	GPIO_InitStructure.GPIO_Pin		= LCD_PINS_DATA ;
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed	= GPIO_Speed_50MHz;
	GPIO_Init( LCD_PORT, &GPIO_InitStructure );

	// Control lines
	GPIO_InitStructure.GPIO_Pin		= LCD_PINS_CONF;
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_Out_PP;
	GPIO_Init( LCD_PORT, &GPIO_InitStructure );

	hd44780_init();
	hd44780_outcmd( HD44780_ENTMODE( 1, 0 ) );
	_delay_ms(50);

	hd44780_outcmd( HD44780_DISPCTL( 1, 0, 0 ) );
	_delay_ms(50);

	hd44780_outcmd( HD44780_DDADDR( 0x00 ) );
	_delay_ms(10);
	xLCDQueue = xQueueCreate( 6, sizeof( xLCDMessage ) );

	xTaskCreate( vTaskLCD, ( signed portCHAR * ) "LCD", stackDepth, NULL, uxPriority, NULL );
}


/*---------------------------------------------------------------------------------------------------------------------*/
static portBASE_TYPE xLCDExecCmd( xLCDMessage * xMessage )
{
	switch( ( LCD_CMD ) xMessage->cText[0] )
	{
		case CMD_DUMMY:
			break;

		case CMD_CLEAR:
			globalPosition = 0;
			hd44780_outcmd( HD44780_CLR );
			_delay_ms( 10 );
			break;

		case CMD_CUR_STATE:
			if( xMessage->cText[1] == LCD_CUR_NONE )
				hd44780_outcmd( HD44780_DISPCTL( 1, 0, 0 ) );
			if( xMessage->cText[1] == LCD_CUR_ON )
				hd44780_outcmd( HD44780_DISPCTL( 1, 1, 0 ) );
			if( xMessage->cText[1] == LCD_CUR_BLINK )
				hd44780_outcmd( HD44780_DISPCTL( 1, 1, 1 ) );
			_delay_ms( 10 );
			break;

		case CMD_INC_OFF:
			break;

		case CMD_INC_ON:
			break;

		case CMD_SET_POS:
			globalPosition = xMessage->cText[1];
			hd44780_outcmd( HD44780_DDADDR( globalPosition ) );
			_delay_ms(10);
			break;

		case CMD_LEFT:
			globalPosition--;
			hd44780_outcmd( HD44780_SHIFT( 0, 0 ) );
			_delay_ms( 10 );
			break;

		case CMD_RIGHT:
			globalPosition++;
			hd44780_outcmd( HD44780_SHIFT( 0, 1 ) );
			_delay_ms( 10 );
			break;

		case CMD_CUR_HOME:
			break;

		case CMD_DIS_OFF:
			break;

		case CMD_DIS_ON:
			break;

		case CMD_DIS_RESET:
			break;
	}
	return LCD_OK;
}

/*---------------------------------------------------------------------------------------------------------------------*/
static portTASK_FUNCTION( vTaskLCD, pvParameters )
{
	(void)pvParameters;

xLCDMessage message;

	for(;;)
	{
		if( xLCDQueue != NULL )
		{
			memset( &message, 0, sizeof( message ) );
			if( xQueueReceive( xLCDQueue, &message, 100 ) == pdTRUE )
			{
				if( message.xDataType == DT_Text )
				{
					hd44780_outcmd( HD44780_DDADDR( message.ucPosition ) );
					_delay_ms(10);
					DrawString( message.cText, message.ucTextLen );
				}

				if( message.xDataType == DT_CMD )
					xLCDExecCmd( &message );
			}
		}
	}
}

